Go 读取模板文件之 template 包
Golang 官方提供了 template 包用来方便用户动态模板文件
下文因为转码的问题, {{
都改成了 {/{
Go语言模板引擎的使用可以分为三部分:定义模板文件、解析模板文件和模板渲染。
按照相应的语法编写模板文件。
<html>
<head>
<title>模板文件</title>
</head>
<body>
hello {\{ . }}
</body>
</html>
Go语言内置了文本模板引擎 text/template
和用于 HTML 文档的 html/template
。它们的作用机制可以简单归纳如下:
- 模板文件通常定义为
.tmpl
和.tpl
为后缀(也可以使用其他的后缀),必须使用 UTF8 编码。 - 模板文件中使用
{\{\}}
包裹和标识需要传入的数据。 - 传给模板的数据可以通过点号
.
来访问,如果数据是复杂类型的数据,可以通过{\{ .FieldName }}
来访问它的字段。 - 除
{\{\}}
包裹的内容外,其他内容均不做修改原样输出。
提供的解析方法
系统定义了一些模板解析方法,获得模板对象
// 创建模板对象,并为其添加一个模板名称
func New(name string) *Template {}
// 解析字符串,比如template.New("name").Parse(src string) 创建模板对象,并完成模板解析。
func (t *Template) Parse(src string) (*Template, error) {}
// 解析模板文件得到模板对象,可以同时解析多个文件
func ParseFiles(filenames ...string) (*Template, error) {}
// 批量解析文件,比如解析当前目录下有以h开头的模板文件:template.ParseGlob("h*")
func ParseGlob(pattern string) (*Template, error) {}
解析模板的时候,有几个实用的方法
// 模板包里面有一个函数Must,它的作用是检测模板是否正确。
// 例如大括号是否匹配,注释是否正确的关闭,变量是否正确的书写
func Must(t *Template, err error) *Template
// Funcs方法向模板t的函数字典里加入参数funcMap内的键值对。
// 如果funcMap某个键值对的值不是函数类型或者返回值不符合要求会panic。
// 但是,可以对t函数列表的成员进行重写。方法返回t以便进行链式调用
func (t *Template) Funcs(funcMapFuncMap) *Template
模板渲染,模板渲染就是将后台的数据替换模板文件中的特定占位符,这里的特定字符是模板文件中的 {\{ . }}
。系统定义了两个渲染方法:
func (t *Template) Execute(wr io.Writer, data interface{}) error {}
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {}
Execute 用来渲染单个模板,如果是用 ParseFiles,ParseGlob 一次性加载了多个模板,就需要用 ExecuteTemplate 选择模板来解析。
模板的语法
在 golang 渲染 template 的时候,可以接受一个 interface{}
类型的变量
有两个常用的传入参数的类型:
- 在模板内可以读取该 struct 域的内容来进行渲染
- map[string]interface{},在模板内可以使用 key 来进行渲染。
模板内内嵌的语法支持,全部需要加 {\{}}
来标记。
在模板文件内,.
代表了当前变量,即在非循环体内,.
就代表了传入的那个变量。假设我们定义了一个结构体:
type Article struct {
ArticleId int
ArticleContent string
}
则可以在模板中
<p>{\{.ArticleContent}}<span>{\{.ArticleId}}</span></p>
同时模板文件也有语法,例如在模板中定义一个变量
{\{$article := .ArticleContent}}
这样就可以把 ArticleContent 字段赋值给 article 变量,这样我们只要使用 {\{$article}}
则可以获取到这个变量的内容。
循环
golang 的 template 支持 range 循环来遍历 map、slice 内的内容,语法为:
{\{range $i, $v := .slice}}
{\{end}}
在这个 range 循环内,我们可以通过 i v 来访问遍历的值
还有一种遍历方式为:
{\{range .slice}}
{\{end}}
这种方式无法访问到 index 或者 key 的值,需要通过 .
来访问对应的 value
{\{range .slice}}
{\{.field}}
{\{end}}
这里使用了 .
来访问遍历的值,那么我们想要在其中访问外部的变量怎么办?(比如渲染模板传入的变量),在这里,我们需要使用 $.
来访问外部的变量
{\{range .slice}}
{\{$.ArticleContent}}
{\{end}}
使用例:
<html>
<head>
<title>模板文件</title>
</head>
<body>
These are error messages:
{\{range $i, $v := .}}
<li> {\{.}}{\{end}}
</body>
</html>
在 Golang 中
import (
_ "embed"
"html/template"
"os"
)
//go:embed static/index.tpl
var content string
func main() {
errors := []string{
"这是错误 1",
"这是错误 2",
"这是错误 3",
"这是错误 4",
"这是错误 5",
"这是错误 6",
}
tmpl := template.New("temp1")
tmpl.Parse(content)
tmpl.Execute(os.Stdout, errors)
}
输出:
<html>
<head>
<title>模板文件</title>
</head>
<body>
These are error messages:
<li> 这是错误 1
<li> 这是错误 2
<li> 这是错误 3
<li> 这是错误 4
<li> 这是错误 5
<li> 这是错误 6
</body>
</html>
调用方法
除了定义变量,在模板文件内,调用方法也非常的简单:
template 包创建新的模板的时候,支持 .Funcs 方法来将自定义的函数集合导入到该模板中,后续通过该模板渲染的文件均支持直接调用这些函数。
该函数集合的定义为:
type FuncMap map[string]interface{}
key 为方法的名字,value 则为函数。这里函数的参数个数没有限制,但是对于返回值有所限制。有两种选择,一种是只有一个返回值,还有一种是有两个返回值,但是第二个返回值必须是 error 类型的。这两种函数的区别是第二个函数在模板中被调用的时候,假设模板函数的第二个参数的返回不为空,则该渲染步骤将会被打断并报错。
在模板文件内,调用方法也非常的简单:
{\{funcname .arg1 .arg2}}
使用例
定义了一个函数
func add(left int, right int) int
则在模板文件内,通过调用
{\{add 1 2}}
就可以获得
3
更多细节参考 golang 模板(template)的常用基本语法
使用例
import (
"fmt"
"os"
"text/template"
)
func main() {
type person struct {
Id int
Name string
Country string
}
user := person{Id: 1001, Name: "jim", Country: "China"}
fmt.Println("user = ", user)
tmpl := template.New("tmpl1")
tmpl.Parse("Hello {\{.Name}} Welcome to go programming...\n")
tmpl.Execute(os.Stdout, user)
}
输出:
user = {1001 jim China}
Hello jim Welcome to go programming...
读取到 String 里面
t := template.New("action")
var err error
t, err = t.ParseFiles("path/to/action.html")
if err != nil {
return err
}
key := "some strings"
data := struct{
Key string
}{
Key: key
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, data); err != nil {
return err
}
result := tpl.String()